package venndiagram;

import java.awt.Point;
import java.lang.Integer;
import java.util.ArrayList;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Adam
 */
public class GraphDataTwoSets implements GraphData
{
    private String aName;
    private String bName;

    private int a;
    private int b;
    private int ab;

    protected CircleData maxCircle;
    protected CircleData minCircle;
    private final double MAX_RADIUS;
    private final int MAX_ELEMENTS;
    private final int MIN_ELEMENTS;
    private ArrayList<Integer> counts;

    public GraphDataTwoSets(String aName, String bName, int a, int b, int ab)
    {
        counts = new ArrayList<Integer>(7);
        
        //Set Names
        this.aName = aName;
        this.bName = bName;

        //Number of elements in each subset
        this.a = a;
        this.b = b;
        this.ab = ab;

        //Initialize max and min circles; set max radius
        if(a >= b)
        {
            //Record number of elements in largest circle
            MAX_ELEMENTS = this.a + this.ab;
            MIN_ELEMENTS = this.b + this.ab;
            
            //store the counts of all the subsets
            setCounts(a, b, ab);
            
            //Create maxCircle based on set a
            MAX_RADIUS = Math.sqrt((this.a + this.ab) / Math.PI);
            this.maxCircle = new CircleData(MAX_RADIUS, new Point(0,0), this.aName);

            //Create minCircle based on set b
           this.minCircle = new CircleData(Math.sqrt((this.b + this.ab) / Math.PI), new Point(0,0), this.bName);
        }
        else
        {
            //Record number of elements in largest circle
            MAX_ELEMENTS = this.b + this.ab;
            MIN_ELEMENTS = this.a + this.ab;
            
            //store the counts of all the subsets
            setCounts(b, a, ab);
            
            //Create maxCircle based on set b
            MAX_RADIUS = Math.sqrt((this.b + this.ab) / Math.PI);
            this.maxCircle = new CircleData(MAX_RADIUS, new Point(0,0), this.bName);

           //Create minCircle based on set a
           this.minCircle = new CircleData(Math.sqrt((this.a + this.ab) / Math.PI), new Point(0,0), this.aName);
        }
        
    }


    @Override
    //Find and set centers for all two circles
    public void findCenters()
    {
        double intToMax = ((double)this.ab / (double)MAX_ELEMENTS);    //Ratio of intersection between max and min circles to max circle

        //Initialize distance so the circles don't intersect
        int distance = (int) (maxCircle.getRadius() + minCircle.getRadius() + 1);

        double intersection;    //Area of intersection of max and min circles

        //Decrease distance until area of intersection at current distance is greater than previously calculated area of intersection
        do
        {
            distance--;
            intersection = findIntersection(maxCircle.getRadius(), minCircle.getRadius(), distance);
        }
        while(intersection / MAX_ELEMENTS < intToMax);

        //Increment distance once if distance + 1 is more accurate than current distance
        if(Math.abs(findIntersection(maxCircle.getRadius(), minCircle.getRadius(), distance + 1) - intersection) <
           Math.abs(findIntersection(maxCircle.getRadius(), minCircle.getRadius(), distance) - intersection))
            distance += 1;

        //Set minCircle center
        minCircle.setCenter(new Point((int)distance,0));
    }
    
    public void resize()
    {   
        //Shift both circles' centers so that all points within both circles contain only positive coordinates
        maxCircle.setCenter(new Point((int) (maxCircle.getCenter().getX() + maxCircle.getRadius()),(int)(maxCircle.getCenter().getY() + maxCircle.getRadius())));
        minCircle.setCenter(new Point((int) (minCircle.getCenter().getX() + minCircle.getRadius()),(int)(minCircle.getCenter().getY() + minCircle.getRadius())));
        
        //Values for determining how to fit diagram to screen
        int screenWidth = 476;  //Width of screen to fit diagram to
        int screenHeight = 357; //Height of screen to fit diagram to
        int padding = 40;       //Amount of padding to allow between edge of screen and diagram
        
        int diagramWidth;   //Width of diagram before resize
        int diagramHeight;  //Height of diagram before resize
        
        //Calculate width of diagram before resize
        diagramWidth = (int)(minCircle.getCenter().getX() + minCircle.getRadius());
        
        //Calculate height of diagram before resize
        diagramHeight = (int)(2 * maxCircle.getRadius());
        
        //Determine whether to fit by width or by height and modify multiplier accordingly
        double multiplier;  //Multiplier to resize diagram by

        //Fit by width
        if(diagramWidth/diagramHeight > screenWidth/screenHeight)
            multiplier = (screenWidth - padding) / diagramWidth;
        //Fit by height
        else
            multiplier = (screenHeight - padding) / diagramHeight;
        
        //Perform resize operation
        int newX = (int) (maxCircle.getCenter().getX() * multiplier);    //New x coordinate for center of circle
        int newY = (int) (maxCircle.getCenter().getY() * multiplier);    //New y coordinate for center of circle
        maxCircle.setCenter(new Point(newX,newY));
        maxCircle.setRadius(maxCircle.getRadius() * multiplier);
        
        newX = (int) (minCircle.getCenter().getX() * multiplier);
        newY = (int) (minCircle.getCenter().getY() * multiplier);
        minCircle.setCenter(new Point(newX,newY));
        minCircle.setRadius(minCircle.getRadius() * multiplier);
        
        //Calculate width of diagram after resize
        diagramWidth = (int)(minCircle.getCenter().getX() + minCircle.getRadius());
        
        //Calculate height of diagram after resize
        diagramHeight = (int)(2 * maxCircle.getRadius());
        
        int shiftToCenterByWidth = (int)((screenWidth - diagramWidth) / 2);
        
        //Shift diagram on screen according to width and padding
        maxCircle.setCenter(new Point((int)(maxCircle.getCenter().getX() + (shiftToCenterByWidth)),((int)(maxCircle.getCenter().getY() + (padding / 2)))));
        minCircle.setCenter(new Point((int)(minCircle.getCenter().getX() + (shiftToCenterByWidth)),((int)(minCircle.getCenter().getY() + (padding / 2)))));
        
    }

    //Find the area of intersection for two circles
    public double findIntersection(double radius1, double radius2, double distance)
    {
        if(distance >= radius1 + radius2)
            return 0.0;
        else if(radius1 != radius2)
            return (radius1 * radius1 * Math.acos((distance * distance + radius1 * radius1 - radius2 * radius2) / (2 * distance * radius1))) +
               (radius2 * radius2 * Math.acos((distance * distance + radius2 * radius2 - radius1 * radius1) / (2 * distance * radius2))) -
               (0.5 * Math.sqrt((radius1 + radius2 - distance) * (radius1 - radius2 + distance) * (radius2 - radius1 + distance) * (radius1 + radius2 + distance)));
        else
            return (2 * radius2 * radius2 * Math.acos(distance / (2 * radius2)) - 0.5 * distance * Math.sqrt(4 * radius2 * radius2 - distance * distance));
    }
    
    //get the name of the largest set
    public String getMaxSetName(){
        return maxCircle.getName();
    }
    
    //get the name of the smallest set
    public String getMinSetName(){
        return minCircle.getName();
    }
    
    //return the arraylist which holds the count of all the subsets
    public ArrayList<Integer> getCounts(){
        return counts;
    }
    
    //sets the counts of all the subsets. null is used for the spots which
    //do not pertain to data with two sets. This order matters because this
    //is the order in which the sets are drawn.
    private void setCounts(Integer max, Integer min, Integer maxMin){
        counts.add(0, max);
        counts.add(1, min);
        counts.add(2, null);
        counts.add(3, maxMin);
        counts.add(4, null);
        counts.add(5, null);
        counts.add(6, null);
    }
}